// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once
#include <cantor/parser/ast/base.h>
#include <cantor/parser/ast/expr.h>
#include <cantor/parser/ast/clause.h>

namespace cantor::parser {
    const int MYSQL_FLOAT_PRECISION = 24;

    enum MysqlType : unsigned char {
        MYSQL_TYPE_DECIMAL = 0,
        MYSQL_TYPE_TINY,
        MYSQL_TYPE_SHORT,
        MYSQL_TYPE_LONG,
        MYSQL_TYPE_FLOAT,
        MYSQL_TYPE_DOUBLE,
        MYSQL_TYPE_NULL,
        MYSQL_TYPE_TIMESTAMP,
        MYSQL_TYPE_LONGLONG,
        MYSQL_TYPE_INT24,
        MYSQL_TYPE_DATE,
        MYSQL_TYPE_TIME,
        MYSQL_TYPE_DATETIME,
        MYSQL_TYPE_YEAR,
        MYSQL_TYPE_NEWDATE,
        MYSQL_TYPE_VARCHAR,
        MYSQL_TYPE_BIT,
        MYSQL_TYPE_TIMESTAMP2,
        MYSQL_TYPE_DATETIME2,
        MYSQL_TYPE_TIME2,
        MYSQL_TYPE_TDIGEST = 242,
        MYSQL_TYPE_BITMAP = 243,
        MYSQL_TYPE_HLL = 244,
        MYSQL_TYPE_JSON = 245,
        MYSQL_TYPE_NEWDECIMAL = 246,
        MYSQL_TYPE_ENUM = 247,
        MYSQL_TYPE_SET = 248,
        MYSQL_TYPE_TINY_BLOB = 249,
        MYSQL_TYPE_MEDIUM_BLOB = 250,
        MYSQL_TYPE_LONG_BLOB = 251,
        MYSQL_TYPE_BLOB = 252,
        MYSQL_TYPE_VAR_STRING = 253,
        MYSQL_TYPE_STRING = 254,
        MYSQL_TYPE_GEOMETRY = 255
    };

    // https://github.com/mysql/mysql-server/blob/8.0/include/mysql_com.h
    enum MysqlFieldFlag {
        MYSQL_FIELD_FLAG_NOT_NULL = 1 << 0,
        MYSQL_FIELD_FLAG_PRI_KEY = 1 << 1,
        MYSQL_FIELD_FLAG_UNIQ_KEY = 1 << 2,
        MYSQL_FIELD_FLAG_MULTI_KEY = 1 << 3,
        MYSQL_FIELD_FLAG_BLOB = 1 << 4,
        MYSQL_FIELD_FLAG_UNSIGNED = 1 << 5,
        MYSQL_FIELD_FLAG_ZEROFILL = 1 << 6,
        MYSQL_FIELD_FLAG_BINARY = 1 << 7,
        MYSQL_FIELD_FLAG_ENUM = 1 << 8,
        MYSQL_FIELD_FLAG_AUTO_INC = 1 << 9,
        MYSQL_FIELD_FLAG_Timestamp = 1 << 10,
        MYSQL_FIELD_FLAG_SET = 1 << 11,
        MYSQL_FIELD_FLAG_NODEFAULTVALUE = 1 << 12,
        MYSQL_FIELD_FLAG_ONUPDATENOW = 1 << 13,
        MYSQL_FIELD_FLAG_PARTKEY = 1 << 14,
        MYSQL_FIELD_FLAG_NUM = 1 << 15
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// EnumElement  - Single element within an ENUM definition
    ///
    /// Inherits from DdlNode with DDLType set to DDL_ENUM_ELEMENT.
    /// Represents one named member in an ENUM type, with an optional constant value.
    /// If `value` is not specified, it is inferred by the system.
    ///
    /// Example:
    ///   ENUM abc { a = 1, b = 2, c }
    ///   -> a and b have explicit values, c is inferred.
    ///
    /// Fields:
    ///   - `name`: Identifier of the Enum member (e.g., "a", "b")
    ///   - `value`: Optional constant integer value (can be nullptr)
    ///
    /// Provides JSON serialization via `to_json()`
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct EnumElement final : public DefiniteExpr {
        EnumElement() : DefiniteExpr(pb::ast::ENT_ENUM_ELEMENT, pb::ast::ENC_DDL) {
            name.set_null();
        }

        /// Convert this EnumElement to JSON representation
        nlohmann::ordered_json to_json() const override;

        /// The name of the Enum element (e.g., "a", "b", "c")
        String name;

        /// Optional value assigned to the Enum element; may be nullptr
        ConstantExpr *value{nullptr};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// ColumnOption - Represents an option associated with a column definition.
    ///
    /// This class is part of the AST and is used during `CREATE TABLE` or
    /// `ALTER TABLE` statements to describe additional properties of a column,
    /// such as DEFAULT value, COMMENT, GENERATED expressions, NULL/NOT NULL constraints, etc.
    ///
    /// The `expr` field varies depending on `type`, and may point to:
    ///   - `FuncExpr*` for functions (e.g., NOW())
    ///   - `ConstantExpr*` for constants
    ///   - nullptr for simple flags like NOT NULL
    ///
    /// Note: This is an AST-only utility class used during parsing and planning.
    ///
    ////////////////////////////////////////////////////////////////////////////////

    struct ColumnOption : public DefiniteExpr {
        /// The specific kind of column option (e.g., DEFAULT, COMMENT, GENERATED)
        pb::ast::ColumnOptionType type{pb::ast::COLUMN_OPT_NONE};

        /// The expression attached to this option, if applicable
        /// May be a ConstantExpr*, FuncExpr*, or nullptr
        DispatchExpr *expr = nullptr;

        /// Indicates whether the generated column is STORED (vs VIRTUAL)
        /// Only applicable when `type` is COLUMN_OPT_GENERATED
        bool store = false;

        /// Convert to JSON (used for debugging)
        nlohmann::ordered_json to_json() const override;

        // ReferenceDef* foreign_ref;

        ColumnOption() : DefiniteExpr(pb::ast::ENT_COLUMN_OPT, pb::ast::ENC_DDL) {
        }

        virtual void print() const override {
        }

        /// Stream serialization for logging/debugging
        void to_stream(std::ostream &os) const override {
            os << "{ type:" << static_cast<int>(type);
            if (expr) {
                os << ", expr:";
                expr->to_stream(os);
            } else {
                os << ", expr:(null)";
            }
            os << "}";
        }
    };

    struct TypeStmt;

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// ColumnDef  - Column definition node for DDL statements
    ///
    /// Represents a column definition in CREATE TABLE / STRUCT / other schema-level constructs.
    /// Stores column name, type, and column-level options (e.g., default, not null, encoding).
    ///
    /// Inherits from DefiniteExpr with ENT_COLUMN_DEF
    ///
    /// Fields:
    ///   - `name`: Column name (may include qualifiers via ColumnName)
    ///   - `type_name`: The declared type of the column (including array, modifiers)
    ///   - `options`: Vector of column-level options (e.g., default value, constraints)
    ///
    /// Methods:
    ///   - `to_stream()`: Textual representation for debugging/logging
    ///   - `to_json()`: Serialize structure to JSON
    ///   - `is_this_type()`: Static utility to check if an DispatchExpr or DdlNode is a ColumnDef
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct ColumnDef : public DefiniteExpr {
        /// Column name, possibly qualified
        ColumnName *name = nullptr;

        /// Column type (including modifiers, arrays, etc.)
        TypeStmt *type_name = nullptr;

        /// List of column options (e.g., NOT NULL, DEFAULT, ENCODING)
        Vector<ColumnOption *> options;

        /// Constructor: set DDLType to ENT_COLUMN_DEF
        ColumnDef() : DefiniteExpr(pb::ast::ENT_COLUMN_DEF, pb::ast::ENC_DDL) {
        }

        /// Print textual form to ostream (for logging/debug)
        void to_stream(std::ostream &os) const override;

        /// Convert this node to a JSON object
        nlohmann::ordered_json to_json() const override;
    };

    inline std::ostream &operator<<(std::ostream &os, const ColumnDef &column) {
        column.to_stream(os);
        return os;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// IndexOption  - Represents options for an index in DDL statements
    ///
    /// Currently holds metadata like comment associated with the index.
    /// Future extensions may include fields such as block size, storage parameters, etc.
    ///
    /// Inherits from DefiniteExpr with DDLType::ENT_INDEX_OPT
    ///
    /// Fields:
    ///   - `comment`: Optional user comment for the index
    ///
    ////////////////////////////////////////////////////////////////////////////////////////////////


    struct IndexOption : public DefiniteExpr {
        //uint64_t block_size;
        //just use comment
        /// User comment describing the index or its purpose
        String comment;
        pb::ast::IndexAlgoType index_type = pb::ast::INDEX_NONE;
        IndexLockOpt* lock{nullptr};
        AlgorithmOpt* algo{nullptr};
        /// Constructor: sets ddl_type to ENT_INDEX_OPT
        IndexOption() : DefiniteExpr(pb::ast::ENT_INDEX_OPT, pb::ast::ENC_DDL) {
            comment = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    inline IndexOption* set_index_type(IndexOption* lhs, pb::ast::IndexAlgoType type, turbo::Arena&arena) {
        if (type == pb::ast::INDEX_NONE || (lhs && lhs->index_type != pb::ast::INDEX_NONE)) {
            return lhs;
        }
        if (lhs == nullptr) {
            lhs = static_cast<IndexOption*>(arena.allocate(sizeof(IndexOption)));
            new(lhs) IndexOption();
        }
        lhs->index_type = type;
        return lhs;
    }

    inline IndexOption* merge_index_option(IndexOption* lhs, IndexOption* rhs) {
        if (lhs == nullptr) return rhs;
        if (rhs == nullptr) return lhs;
        if (rhs->lock == nullptr && lhs->lock != nullptr) {
            rhs->lock = lhs->lock;
        }
        if (rhs->comment.empty() && !lhs->comment.empty()) {
            rhs->comment = lhs->comment;
        }
        if (rhs->index_type == pb::ast::INDEX_NONE) {
            rhs->index_type = lhs->index_type;
        }
        if (rhs->algo == nullptr && lhs->algo != nullptr) {
            lhs->algo = lhs->algo;
        }
        return rhs;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// TableOption  - Represents an option or property for a table in DDL statements
    ///
    /// Used to specify various configurable options for table creation or alteration.
    /// Each option can hold either a string value or an unsigned integer value.
    ///
    /// Inherits from DefiniteExpr with ENT_TABLE_OPT
    ///
    /// Fields:
    ///   - `type`: Specifies the kind of table option (Enum TableOptionType)
    ///   - `str_value`: String representation of the option's value, if applicable
    ///   - `uint_value`: Unsigned integer representation of the option's value, if applicable
    ///
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct TablePartitionOption;

    struct TableOption : public DefiniteExpr {
        /// The type of table option this struct represents
        pb::ast::TableOptionType type{pb::ast::TableOptionType::TABLE_OPT_NONE};

        /// String value for options represented as text
        String str_value;

        /// Numeric value for options represented as integer
        uint64_t uint_value{0};

        nlohmann::ordered_json to_json() const override;

        TablePartitionOption* partition{nullptr};
        /// Constructor: sets ddl_type to ENT_TABLE_OPT
        TableOption() : DefiniteExpr(pb::ast::ENT_TABLE_OPT, pb::ast::ENC_DDL) {
            str_value.set_null();
        }
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// PartitionOption  - Represents an option or property for table partitions in DDL statements
    ///
    /// Used to specify configuration options related to partitions during table creation or alteration.
    ///
    /// Inherits from DefiniteExpr with ENT_PARTITION_OPT
    ///
    /// Fields:
    ///   - `type`: Specifies the kind of partition option (Enum PartitionOptionType)
    ///   - `str_value`: String representation of the option's value
    ///
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct PartitionOption final : public DefiniteExpr {
        /// The type of partition option this struct represents
        pb::ast::PartitionOptionType type{pb::ast::PARTITION_OPT_NONE};

        /// String value for the partition option
        String str_value;

        /// Constructor: sets ddl_type to ENT_PARTITION_OPT
        PartitionOption() : DefiniteExpr(pb::ast::ENT_PARTITION_OPT, pb::ast::ENC_DDL) {
            str_value.set_null();
        }

        nlohmann::ordered_json to_json() const override;
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// PartitionRange  - Represents a range partition definition in DDL statements
    ///
    /// Defines a named partition with a specific range of values and associated options.
    ///
    /// Inherits from DefiniteExpr with DDLType::ENT_PARTITION_RANGE
    ///
    /// Fields:
    ///   - `name`: The name identifier of the partition
    ///   - `less_expr`: Expression defining the upper bound of the partition range (less than)
    ///   - `range`: A pair of expressions specifying the start and end of the range (inclusive or exclusive depending on semantics)
    ///   - `options`: Additional partition options configured for this range (e.g., storage parameters)
    ///
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct PartitionRange : public DefiniteExpr {
        /// Constructor: sets ddl_type to ENT_PARTITION_RANGE
        PartitionRange() : DefiniteExpr(pb::ast::ENT_PARTITION_RANGE, pb::ast::ENC_DDL) {
            name.set_null();
        }

        nlohmann::ordered_json to_json() const override;

        /// Name of this partition range
        String name;

        /// Expression for the "less than" bound of the partition
        DispatchExpr *less_expr{nullptr};

        /// Pair of expressions representing range boundaries (start, end)
        std::pair<DispatchExpr *, DispatchExpr *> range{nullptr, nullptr};
        /// List of options associated with this partition range
        Vector<PartitionOption *> options;
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// TablePartitionOption  - Represents options and configuration for table partitioning
    ///
    /// Inherits from DefiniteExpr with ENT_TABLE_PARTITION_OPTION
    ///
    /// Fields:
    ///   - `type`: The type of table partition option being specified
    ///   - `str_value`: String value associated with the option (e.g., partition strategy name)
    ///   - `uint_value`: Numeric value related to the option (e.g., threshold or limit)
    ///   - `partition_type`: Enum indicating the partitioning method/type (e.g., RANGE, LIST)
    ///   - `expr`: Expression related to partitioning (e.g., partition key expression)
    ///   - `partition_num`: Number of partitions, defaulting to 1
    ///   - `ranges`: Vector of PartitionRange pointers defining the specific ranges (for range/list partitions)
    ///
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct TablePartitionOption : public DefiniteExpr {
        /// Constructor: sets ddl_type to ENT_TABLE_PARTITION_OPTION
        TablePartitionOption() : DefiniteExpr(pb::ast::ENT_TABLE_PARTITION_OPTION, pb::ast::ENC_DDL) {
        }

        nlohmann::ordered_json to_json() const override;

        /// Type of partitioning used (RANGE, LIST, HASH, etc.)
        pb::ast::PartitionType partition_type{pb::ast::PartitionType::PARTITION_HASH};

        /// Expression used in partitioning key or criteria
        DispatchExpr *expr = nullptr;

        /// Number of partitions (default 1)
        int64_t partition_num = 1;

        /// List of partition ranges defined under this option
        Vector<PartitionRange *> ranges;
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// Constraint  - Represents table or column constraints such as primary key, unique, etc.
    ///
    /// Inherits from DdlNode with DDLType::DDL_CONSTRAINT
    ///
    /// Fields:
    ///   - `type`: Type of constraint (PRIMARY KEY, UNIQUE, FOREIGN KEY, FULLTEXT, etc.)
    ///   - `name`: Optional name of the constraint (can be nullptr)
    ///   - `columns`: Vector of column references involved in the constraint (used for PRIMARY KEY, UNIQUE, FULLTEXT, KEY)
    ///   - `index_option`: Optional pointer to index-related options for the constraint
    ///   - `index_dist`: Specifies index distribution type, defaults to INDEX_DIST_DEFAULT
    ///
    /// Static helper methods:
    ///   - `is_this_type(DispatchExpr *node)`: Checks if an DispatchExpr is a Constraint node
    ///   - `is_this_type(DdlNode *node)`: Checks if a DdlNode is a Constraint node
    ///
    ////////////////////////////////////////////////////////////////////////////////////////////////

    struct Constraint : public DefiniteExpr {
        /// The type of constraint
        cantor::pb::ast::ConstraintType type{cantor::pb::ast::ConstraintType::CONSTRAINT_NONE};

        /// Optional constraint name, nullptr if unnamed
        String name;

        /// Columns involved in the constraint
        Vector<ColumnName *> columns; // Used for PRIMARY KEY, KEY, UNIQUE, FULLTEXT

        // ReferenceDef*   refer = nullptr;    // Used for foreign key.
        /// Index options associated with this constraint (optional)
        IndexOption *index_option = nullptr;

        DispatchExpr * check_expr = nullptr;

        /// Index distribution type, default is INDEX_DIST_DEFAULT
        pb::ast::IndexDistributeType index_dist = pb::ast::INDEX_DIST_DEFAULT;

        Constraint() : DefiniteExpr(pb::ast::ENT_CONSTRAINT, pb::ast::ENC_DDL) {
            name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    /// TableDefElement represents an element in a CREATE TABLE definition.
    ///
    /// In the parsing phase, each element inside the table definition (between the parentheses)
    /// may be either a column definition or a table constraint.
    /// Because at parse time it is ambiguous, this node acts as an intermediate container.
    ///
    /// Only one of `column` or `constrain` is set at a time to represent the actual element.
    ///
    /// Example:
    /// ```sql
    /// CREATE TABLE example (
    ///   id INT PRIMARY KEY,
    ///   name VARCHAR(100),
    ///   UNIQUE KEY (name)
    /// );
    /// ```
    ///
    /// During parsing, each line inside the parentheses corresponds to a TableDefElement,
    /// which will later be resolved into a ColumnDef or Constraint node.
    ///
    /// This node is not meant to be serialized or used beyond the parsing stage.

    struct TableDefElement : public DefiniteExpr {
        TableDefElement() : DefiniteExpr(pb::ast::ENT_TABLE_DEF_ELEMENT, pb::ast::ENC_DDL) {
        }

        /// Override to_json to prevent usage on intermediate AST nodes.
        ///
        /// This node is only meant for parsing stage and should never be serialized directly.
        /// Calling to_json on this node will trigger a runtime assertion failure.

        nlohmann::ordered_json to_json() const override {
            KCHECK(false) << "you should not using this for ast node";
        }

        /// Points to a column definition if this element represents a column.
        /// Only one of `column` or `constrain` can be set at a time.
        ColumnDef *column{nullptr};
        Constraint *constrain{nullptr};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// DatabaseOption - AST node representing an option within a CREATE DATABASE statement.
    ///
    /// This node is used to capture optional parameters such as CHARACTER SET,
    /// COLLATE, or COMMENT when defining a database.
    ///
    /// Currently supported options are:
    ///   - DATABASE_OPT_CHARSET   : Character set of the database
    ///   - DATABASE_OPT_COLLATE   : Collation rules for string comparison
    ///   - DATABASE_OPT_COMMENT   : Descriptive comment for the database
    ////////////////////////////////////////////////////////////////////////////////

    struct DatabaseOption : public DefiniteExpr {
        /// The type of the database option (e.g., charset, collate, comment)
        pb::ast::DatabaseOptionType type{pb::ast::DatabaseOptionType::DATABASE_OPT_NONE};

        /// The value associated with the option (e.g., 'utf8mb4', 'en_US', 'analytics db')
        String str_value;

        DatabaseOption() : DefiniteExpr(pb::ast::ENT_DATABASE_OPT, pb::ast::ENC_DDL) {
            str_value = nullptr;
        }
        nlohmann::ordered_json to_json() const override;
    };

    struct Assignment;

    ////////////////////////////////////////////////////////////////////////////////
    /// AlterTableSpec - Represents an individual specification in an ALTER TABLE statement.
    ///
    /// This struct models the various types of ALTER TABLE operations supported,
    /// such as adding/dropping columns, indexes, constraints, partitions, and more.
    /// It encapsulates all relevant data needed to describe and execute the alteration.
    ///
    /// Example:
    /// ALTER TABLE mytable ADD COLUMN new_col INT NOT NULL,
    /// DROP INDEX idx_old,
    /// RENAME TO mytable_new;
    ////////////////////////////////////////////////////////////////////////////////
    struct WhereClause;
    struct AlterTableSpec : public DefiniteExpr {
        /// The type of ALTER specification (e.g., ADD COLUMN, DROP INDEX)
        pb::ast::AlterSpecType spec_type{pb::ast::ALTER_SPEC_ADD_COLUMN};

        /// Column name involved in operations such as ADD, DROP, RENAME, etc.
        String column_name;

        /// Table-level options, e.g., ENGINE, CHARSET
        Vector<TableOption *> table_options;

        /// New table name in case of RENAME TABLE
        TableName *new_table_name = nullptr;

        /// New column definitions for ADD/MODIFY/CHANGE COLUMN
        Vector<ColumnDef *> new_columns;

        /// Used in ALTER with SET clause
        Vector<Assignment *> set_list;

        /// Optional WHERE clause for conditional ALTERs
        WhereClause *where = nullptr;

        /// Constraints to add (e.g., PRIMARY KEY, UNIQUE)
        Vector<Constraint *> new_constraints;

        /// Index name, used in ADD/DROP INDEX
        String index_name;

        /// Optional resource tag for physical placement hints
        String resource_tag;

        /// Whether to force the operation (engine-specific behavior)
        bool force = false;

        /// Whether the index is a virtual (non-materialized) index
        bool is_virtual_index = false;

        /// Whether the added index is a uniqueness indicator
        bool is_unique_indicator = false; // for re

        /// Partition details for ALTER PARTITION operations
        PartitionRange *partition_range = nullptr;

        AlterTableSpec() : DefiniteExpr(pb::ast::ENT_ALTER_SEPC, pb::ast::ENC_DDL) {
            column_name = nullptr;
            index_name = nullptr;
            resource_tag = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };
} // namespace cantor::parser
